import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;

public class PlainTextAttack {

	
	private char[][] m_keyArray = new char[52][2];
//	private char[] m_letterArray = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
//			'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
//			'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
//			'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
//			'V', 'W', 'X', 'Y', 'Z' };
	static String m_letterString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private int m_sizeOfInitialVector;
	private byte[][] m_blocks;
	private int m_tens;
	private byte[] m_initialVector;
	private byte[] m_encrypted;
	private byte[][][] m_decrypted;
	private byte[][][] m_tryThis;
	private byte[] m_knownText;
	private byte[] m_knownChiper;
	private int m_countLetters = 0;
	private int[][] m_key;
	private String m_notFound;
	private String m_found;
	private String m_notUse;

	public PlainTextAttack(byte[] text, byte[] key, byte[] initialVector,
			int initialVectorSize) throws IOException {
		m_sizeOfInitialVector = initialVectorSize;
		m_tens = 0;
		m_tens = 0;
		if (text.length % 8128 != 0)
			m_tens = 1;
		m_blocks = new byte[text.length / 8128 + m_tens][8128];// create room
																// for
																// the text by
																// blocks
		MoveToBlocks(text);// move to block of ten
		m_initialVector = initialVector;
		int i;
		for (i = 0; i < 52; i++) {// puts in the array
			m_keyArray[i][0] = m_letterString.charAt(i);
		}
	}

	public PlainTextAttack(byte[] text, byte[] initialVector, byte[] knownText,
			byte[] knownChiper, int initialVectorSize) throws IOException {
		m_sizeOfInitialVector = initialVectorSize;
		m_tens = 0;
		if (text.length % 8128 != 0)
			m_tens = 1;
		m_blocks = new byte[text.length / 8128 + m_tens][8128];// create room
																// for
																// the text by
																// blocks
		m_initialVector = initialVector;
		m_knownText = knownText;
		m_knownChiper = knownChiper;
		MoveToBlocks(text);// move to block of ten
		int i;

		for (i = 0; i < 52; i++) {// puts in the array
			m_keyArray[i][0] = m_letterString.charAt(i);
		}
		m_encrypted = new byte[8128];
		InitialDeciphering();
	}

	private void InitialDeciphering() {
		int i;
		for (i = 0; i < m_sizeOfInitialVector && i < m_knownText.length; i++) {
			m_encrypted[i] = (byte) (m_knownText[i] ^ m_initialVector[i]);
			if ((m_encrypted[i] > 'a' - 1 && m_encrypted[i] < 'z' + 1)
					|| (m_encrypted[i] > 'A' - 1 && m_encrypted[i] < 'Z' + 1)) {
				if (m_keyArray[m_letterString.indexOf(m_encrypted[i])][1] == '\0') {
					m_keyArray[m_letterString.indexOf(m_encrypted[i])][1] = (char) (m_knownChiper[i]);
				}
			}
		}	
		NotFound();
		NotUse();
	}

	private void NotFound() {
		int i;
		
		m_notFound = new String();
		m_found = new String();
		m_countLetters = 0;
		for (i = 0; i < 52; i++) {
			if (m_keyArray[i][1] == '\0') {
				m_notFound = m_notFound + m_keyArray[i][0];
			} else {
				m_countLetters++;
				m_found = m_found + m_keyArray[i][1];
			}
		}
		m_key = new int[52 - m_countLetters][52 - m_countLetters];
	}

	private void NotUse() {
		m_notUse = new String();
		int i;
		for (i = 0; i < 52; i++) {
			if (m_found.indexOf(m_letterString.charAt(i)) == -1) {
				m_notUse = m_notUse + m_letterString.charAt(i);
			}
		}
	}

	public void Encrypt(String name,String key) throws IOException {
		EncryptionKey(key);// generate a key
		int i, j;
		m_encrypted = new byte[8128];
		for (i = 0; i < m_sizeOfInitialVector; i++)
			m_encrypted[i] = m_initialVector[i];
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < m_blocks[i].length; j++) {
				m_encrypted[j] = (byte) (m_blocks[i][j] ^ m_encrypted[j]);
				if ((m_encrypted[j] > 'a' - 1 && m_encrypted[j] < 'z' + 1)
						|| (m_encrypted[j] > 'A' - 1 && m_encrypted[j] < 'Z' + 1))
					ChangeToEncryptedKey(j);
			}
			FileWriter file = new FileWriter(name.substring(0,name.length()-4) + ".cipher", true);
			file.write(new String(m_encrypted));
			file.close();
		}
	}

	private void EncryptionKey(String key) {
		int i;
		Boolean check=false;
		char pos=' ';
		Boolean position= true;
		for (i = 0; i <key.length(); i++) {
				if(key.charAt(i)==' ' || key.charAt(i)=='\n')
					continue;
				else
				{
					if(check==false)
					{
						
						pos=key.charAt(i);
						check =true;
						if(((int)pos>64&&(int)pos<91))
								position=false;
					}
					else
					{
						check=false;
						if(position==false)
							m_keyArray[(int)(pos-65+26)][1] =key.charAt(i);
						else
							m_keyArray[pos-'a'][1] =key.charAt(i);
						position=true;
					}
				}
		}
	}
	
	private void ChangeToEncryptedKey(int i) {
		m_encrypted[i] = (byte) m_keyArray[m_letterString
				.indexOf(m_encrypted[i])][1];
	}

	public void Decrypt() throws IOException {
		int i, j,k;
		boolean found;
		m_encrypted = new byte[8128];
		m_decrypted = new byte[m_blocks.length][8128][2];
		m_tryThis = new byte[m_blocks.length][8128][2];
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < 8128; j++) {
				m_encrypted[j] = m_blocks[i][j];
			}
			for (j = 0; j < 8128; j++) {
				if ((m_encrypted[j] >= 'A' && m_encrypted[j] <= 'Z')
						|| (m_encrypted[j] >= 'a' && m_encrypted[j] <= 'z')) {
					found = false;
					for(k = 0 ;k < 52 && found ;k++){
						if(m_keyArray[k][1] == m_encrypted[j] ){
							found = true;
							m_decrypted[i][j][1] = -1;
							m_decrypted[i][j][0] = (byte) (m_keyArray[k][0] ^ m_initialVector[j]);
						}
					}
					if(!found){
						m_decrypted[i][j][1] = m_initialVector[j];
						m_decrypted[i][j][0] = m_encrypted[j];
					}
				} else {
					m_decrypted[i][j][1] = -1;
					m_decrypted[i][j][0] = (byte) (m_initialVector[j] ^ m_encrypted[j]);
					m_tryThis[i][j][0] = (byte) (m_initialVector[j] ^ m_encrypted[j]);
				}
			}
			m_initialVector = m_blocks[i];
		}
		TryThis();
		return;
	}

	private void TryThis() {
		int i, j, k,max = Integer.MIN_VALUE;
		byte temp = 0;
		for (i = 0; i < m_decrypted.length; i++) {
			for (j = 0; j < 8128; j++) {
				if (m_notUse.indexOf(m_decrypted[i][j][0]) > -1 && m_decrypted[i][j][1] != -1) {
					for (k = 0; k < 52 - m_countLetters; k++) {
						temp = (byte) ((m_notFound.charAt(k)) ^ m_decrypted[i][j][1]);
							if ( ((temp > 31 && temp < 127))) {
								m_key[m_notUse.indexOf(m_decrypted[i][j][0])][k] += 1;
							} else {		
									if((temp > 0 && temp < 32)  && !(temp == 13 || temp == 9 || temp == 10 ))
										m_key[m_notUse.indexOf(m_decrypted[i][j][0])][k] -= 100;
						}
					}
				}
			}
		}
		for (i = 0; i < 52 - m_countLetters; i++) {
			max = Integer.MIN_VALUE;
			for (j = 0; j < 52 - m_countLetters; j++) {
				if (m_key[i][j] > max) {
					max = m_key[i][j];
					temp = (byte) j;
				}
				
			}
			m_keyArray[m_letterString.indexOf(m_notFound.charAt((int)temp))][1] = (char) (m_notUse
					.charAt(i));
		}
		NotFound();
		NotUse();
	}


	private void MoveToBlocks(byte[] text) {
		int i, j;
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < 8128; j++) {
				if (text.length > i * 8128 + j)// check if length of the array
												// is
												// bigger or it needs to put
												// zero
					m_blocks[i][j] = text[i * 8128 + j];// puts the value inside
														// of it
				else
					m_blocks[i][j] = 0;// if bigger puts zero
			}
		}
	}
	public char[] GetKey() {
		int i;
		char[] res = new char[52];
		for (i = 0; i < 52; i++)
			res[i] = m_keyArray[i][1];
		return res;
	}

	public static void main(String[] args) throws IOException {
		int i;
		String start = args[0];
		if (start.equals("DecryptionB")) {
			File first = new File(args[1]);
			File second = new File(args[2]);
			File third = new File(args[3]);
			File fourth = new File(args[4]);
			int initialVectorSize = 8128;

			byte[] text = Files.readAllBytes(third.toPath());// reads the file
																// to
																// bytes

			byte[] initialVector = Files.readAllBytes(fourth.toPath());
			byte[] knownText = Files.readAllBytes(first.toPath());
			byte[] chiperKnow = Files.readAllBytes(second.toPath());
			PlainTextAttack toEncrypt = new PlainTextAttack(text,
					initialVector, knownText, chiperKnow, initialVectorSize);
			toEncrypt.Decrypt();
			char[] res = toEncrypt.GetKey();
			 FileWriter writer = new FileWriter(args[3] +"_Key.txt");
			for (i = 0; i < res.length; i++) {
				writer.append (m_letterString.charAt(i) +" " + res[i] + '\n');
			}
			 writer.close();
		}
		if (start.equals("EncryptionB")) {
			File first = new File(args[1]);
			File second = new File(args[2]);
			File third = new File(args[3]);
			byte[] key = Files.readAllBytes(second.toPath());
			int initialVectorSize = 8128;
			byte[] text = Files.readAllBytes(first.toPath());// reads the file
																// to
																// bytes

			byte[] initialVector = Files.readAllBytes(third.toPath());
			PlainTextAttack toEncrypt = new PlainTextAttack(text, key,
					initialVector, initialVectorSize);
			toEncrypt.Encrypt(args[1],new String(key));
		}
	}
}
